﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Web.Services.Protocols;
using System.Windows.Forms;
using System.Xml;
using McTools.Xrm.Connection;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Tanguy.WinForm.Utilities.DelegatesHelpers;
using ViewLayoutReplicator2011.Helpers;

namespace ViewLayoutReplicator2011
{
    public partial class MainForm : Form
    {
        #region Variables

        ConnectionManager cManager;

        CrmConnectionStatusBar ccsb;

        IOrganizationService service;

        XmlDocument custoDoc;

        /// <summary>
        /// List of entities
        /// </summary>
        List<EntityMetadata> entitiesCache;

        /// <summary>
        /// List of views
        /// </summary>
        Dictionary<Guid, Entity> viewsList;

        #endregion

        #region Constructor

        public MainForm()
        {
            InitializeComponent();

            this.Text += " (v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + ")"; 
            
            this.cManager = new ConnectionManager(this);
            this.cManager.ConnectionSucceed += new ConnectionManager.ConnectionSucceedEventHandler(cManager_ConnectionSucceed);
            this.cManager.ConnectionFailed += new ConnectionManager.ConnectionFailedEventHandler(cManager_ConnectionFailed);
            this.cManager.StepChanged += new ConnectionManager.StepChangedEventHandler(cManager_StepChanged);

            ccsb = new CrmConnectionStatusBar(this.cManager);
            this.Controls.Add(ccsb);

            this.FillImagesInListView();
        }

        #endregion

        #region Connections Methods

        void cManager_StepChanged(object sender, StepChangedEventArgs e)
        {
            this.ccsb.SetMessage(e.CurrentStep);
        }

        void cManager_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            this.ccsb.SetMessage(e.FailureReason);
            MessageBox.Show(e.FailureReason, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        void cManager_ConnectionSucceed(object sender, ConnectionSucceedEventArgs e)
        {
            this.service = e.OrganizationService;

            try
            {
                this.ccsb.SetConnectionStatus(true, e.ConnectionDetail);
                CommonDelegates.SetCursor(this, Cursors.WaitCursor);
                this.ccsb.SetMessage("Loading entities...");

                // Caching entities
                this.entitiesCache = MetadataHelper.RetrieveEntities(this.service);

                this.ccsb.SetMessage("Displaying entities...");

                // Filling entities list
                this.FillEntitiesList();

                this.ccsb.SetMessage(string.Empty);
                CommonDelegates.SetCursor(this, Cursors.Default);
            }
            catch (Exception error)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(error, true);
                CommonDelegates.DisplayMessageBox(this, errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Main ToolStrip Handlers

        #region Fill Entities

        private void tsbLoadEntities_Click(object sender, EventArgs e)
        {
            if (this.service == null)
            {
                this.cManager.AskForConnection(null);
            }
            else
            {
                this.lvEntities.Items.Clear();
                this.gbEntities.Enabled = false;
                this.tsbPublishEntity.Enabled = false;
                this.tsbSaveViews.Enabled = false;

                this.lvSourceViews.Items.Clear();
                this.lvTargetViews.Items.Clear();
                this.lvSourceViewLayoutPreview.Columns.Clear();
                this.txtSourceXmlLayout.Text = string.Empty;
                this.txtTargetXmlLayout.Text = string.Empty;

                BackgroundWorker bwFillEntities = new BackgroundWorker();
                bwFillEntities.DoWork += new DoWorkEventHandler(bwFillEntities_DoWork);
                bwFillEntities.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwFillEntities_RunWorkerCompleted);
                bwFillEntities.RunWorkerAsync();
            }
        }

        void bwFillEntities_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(e.Error, true);
                CommonDelegates.DisplayMessageBox(this, errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        void bwFillEntities_DoWork(object sender, DoWorkEventArgs e)
        {
            CommonDelegates.SetCursor(this, Cursors.WaitCursor);
            this.ccsb.SetMessage("Loading entities ...");

            // Caching entities
            this.entitiesCache = MetadataHelper.RetrieveEntities(this.service);

            this.ccsb.SetMessage("Displaying entities...");

            // Filling entities list
            this.FillEntitiesList();

            this.ccsb.SetMessage(string.Empty);
            CommonDelegates.SetCursor(this, Cursors.Default);
        }

        /// <summary>
        /// Fills the entities listview
        /// </summary>
        public void FillEntitiesList()
        {
            try
            {
                ListViewDelegates.ClearItems(this.lvEntities);

                foreach (EntityMetadata emd in entitiesCache)
                {
                    ListViewItem item = new ListViewItem();
                    item.Text = emd.DisplayName.UserLocalizedLabel.Label;
                    item.Tag = emd.LogicalName;
                    ListViewDelegates.AddItem(this.lvEntities, item);
                }

                ColumnHeader header = new ColumnHeader()
                {
                    Text = "Display name",
                    Width = 160
                };

                ListViewDelegates.AddColumn(this.lvEntities, header);
                GroupBoxDelegates.SetEnableState(this.gbEntities, true);
            }
            catch (Exception error)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(error, true);
                CommonDelegates.DisplayMessageBox(this, errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Save Views

        private void tsbSaveViews_Click(object sender, EventArgs e)
        {
            this.tsbPublishEntity.Enabled = false;
            this.tsbSaveViews.Enabled = false;
            this.tsbLoadEntities.Enabled = false;

            //this.Cursor = Cursors.WaitCursor;
            CommonDelegates.SetCursor(this, Cursors.WaitCursor);
            this.ccsb.SetMessage("Copying source view layout to checked target views...");

            BackgroundWorker bwSaveViews = new BackgroundWorker();
            bwSaveViews.DoWork += new DoWorkEventHandler(bwSaveViews_DoWork);
            bwSaveViews.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwSaveViews_RunWorkerCompleted);
            bwSaveViews.RunWorkerAsync();
        }

        void bwSaveViews_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            CommonDelegates.SetCursor(this, Cursors.Default);
            //this.Cursor = Cursors.Default;
            this.ccsb.SetMessage(string.Empty);

            this.tsbPublishEntity.Enabled = true;
            this.tsbSaveViews.Enabled = true;
            this.tsbLoadEntities.Enabled = true;
        }

        void bwSaveViews_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Entity sourceView = (Entity)ListViewDelegates.GetSelectedItems(this.lvSourceViews)[0].Tag;

                List<Entity> targetViews = new List<Entity>();

                foreach (ListViewItem item in ListViewDelegates.GetCheckedItems(this.lvTargetViews))
                {
                    targetViews.Add((Entity)item.Tag);
                }

                if (ViewHelper.PropagateLayout(sourceView, targetViews, this.service))
                {
                    CommonDelegates.DisplayMessageBox(this, "Checked target views updated!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    CommonDelegates.DisplayMessageBox(this, "Checked target views updated!\n\nThe associated view has not been updated because of related attributes", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
            catch (Exception error)
            {
                CommonDelegates.DisplayMessageBox(this, error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Publish Entity

        private void tsbPublishEntity_Click(object sender, EventArgs e)
        {
            if (this.lvEntities.SelectedItems.Count > 0)
            {
                this.tsbPublishEntity.Enabled = false;
                this.tsbSaveViews.Enabled = false;
                this.tsbLoadEntities.Enabled = false;

                CommonDelegates.SetCursor(this, Cursors.WaitCursor);
                //this.Cursor = Cursors.WaitCursor;
                this.ccsb.SetMessage("Publishing entity...");

                BackgroundWorker bwPublish = new BackgroundWorker();
                bwPublish.DoWork += new DoWorkEventHandler(bwPublish_DoWork);
                bwPublish.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwPublish_RunWorkerCompleted);
                bwPublish.RunWorkerAsync(this.lvEntities.SelectedItems[0].Text);
            }
        }

        void bwPublish_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.ccsb.SetMessage(string.Empty);
            CommonDelegates.SetCursor(this, Cursors.Default);
            //this.Cursor = Cursors.Default;
                
            this.tsbPublishEntity.Enabled = true;
            this.tsbSaveViews.Enabled = true;
            this.tsbLoadEntities.Enabled = true;
        }

        void bwPublish_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                EntityMetadata currentEmd = entitiesCache.Find(delegate(EntityMetadata emd) { return emd.DisplayName.UserLocalizedLabel.Label == e.Argument.ToString(); });

                PublishXmlRequest pubRequest = new PublishXmlRequest();
                pubRequest.ParameterXml = string.Format(@"<importexportxml>
                                                           <entities>
                                                              <entity>{0}</entity>
                                                           </entities>
                                                           <nodes/><securityroles/><settings/><workflows/>
                                                        </importexportxml>", currentEmd.LogicalName);

                service.Execute(pubRequest);

                CommonDelegates.DisplayMessageBox(this, "Entity published!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception error)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(error, false);
                CommonDelegates.DisplayMessageBox(this, error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Display About Form

        private void tsbAbout_Click(object sender, EventArgs e)
        {
            AboutForm aForm = new AboutForm();
            aForm.StartPosition = FormStartPosition.CenterParent;
            aForm.ShowDialog();
        }

        #endregion

        #endregion

        #region ListViews Handlers

        #region Fill Views

        private void lvEntities_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.lvEntities.SelectedItems.Count > 0)
            {
                string entityLogicalName = this.lvEntities.SelectedItems[0].Tag.ToString();

                // Reinit other controls
                this.lvSourceViews.Items.Clear();
                this.txtSourceXmlLayout.Text = string.Empty;
                this.lvSourceViewLayoutPreview.Columns.Clear();
                this.lvTargetViews.Items.Clear();
                this.txtTargetXmlLayout.Text = string.Empty;

                this.Cursor = Cursors.WaitCursor;
                this.ccsb.SetMessage("Retrieving views...");

                // Launch treatment
                BackgroundWorker bwFillViews = new BackgroundWorker();
                bwFillViews.DoWork += new DoWorkEventHandler(bwFillViews_DoWork);
                bwFillViews.RunWorkerAsync(entityLogicalName);
                bwFillViews.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwFillViews_RunWorkerCompleted);
            }
        }

        void bwFillViews_DoWork(object sender, DoWorkEventArgs e)
        {
            string entityLogicalName = e.Argument.ToString();

            List<Entity> viewsList = ViewHelper.RetrieveViews(entityLogicalName, this.entitiesCache, this.service);

            foreach (Entity view in viewsList)
            {
                bool display = true;

                ListViewItem item = new ListViewItem(view["name"].ToString());
                item.Tag = view;

                #region Gestion de l'image associée à la vue

                switch ((int)view["querytype"])
                {
                    case ViewHelper.VIEW_BASIC:
                        {
                            if ((bool)view["isdefault"])
                            {
                                item.SubItems.Add("Default public view");
                                item.ImageIndex = 3;
                            }
                            else
                            {
                                item.SubItems.Add("Public view");
                                item.ImageIndex = 0;
                            }
                        }
                        break;
                    case ViewHelper.VIEW_ADVANCEDFIND:
                        {
                            item.SubItems.Add("Advanced find view");
                            item.ImageIndex = 1;
                        }
                        break;
                    case ViewHelper.VIEW_ASSOCIATED:
                        {
                            item.SubItems.Add("Associated view");
                            item.ImageIndex = 2;
                        }
                        break;
                    case ViewHelper.VIEW_QUICKFIND:
                        {
                            item.SubItems.Add("QuickFind view");
                            item.ImageIndex = 5;
                        }
                        break;
                    case ViewHelper.VIEW_SEARCH:
                        {
                            item.SubItems.Add("Lookup view");
                            item.ImageIndex = 4;
                        }
                        break;
                    default:
                        {
                            display = false;
                        }
                        break;
                }

                #endregion

                if (display)
                {
                    // Add view to each list of views (source and target)
                    ListViewItem clonedItem = (ListViewItem)item.Clone();
                    ListViewDelegates.AddItem(lvSourceViews, item);

                    if (view.Contains("iscustomizable") && ((BooleanManagedProperty)view["iscustomizable"]).Value == false)
                    {
                        clonedItem.ForeColor = Color.Gray;
                        clonedItem.ToolTipText = "This view has not been defined as customizable";
                    }
                 
                    ListViewDelegates.AddItem(lvTargetViews, clonedItem);
                }
            }
        }

        void bwFillViews_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                this.Cursor = Cursors.Default;
                this.ccsb.SetMessage(string.Empty);
                this.gbSourceViews.Enabled = true;
                this.gbTargetViews.Enabled = true;
            }

            if (this.lvSourceViews.Items.Count == 0)
            {
                MessageBox.Show(this, "This entity does not contain any view", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        #endregion

        #region Display View

        private void lvSourceViews_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            this.lvSourceViewLayoutPreview.Columns.Clear();

            if (lvSourceViews.SelectedItems.Count > 0)
            {
                this.lvSourceViews.SelectedIndexChanged -= new EventHandler(lvSourceViews_SelectedIndexChanged);

                this.lvSourceViews.Enabled = false;
                this.Cursor = Cursors.WaitCursor;
                this.ccsb.SetMessage("Building selected view...");

                BackgroundWorker bwDisplayView = new BackgroundWorker();
                bwDisplayView.DoWork += new DoWorkEventHandler(bwDisplayView_DoWork);
                bwDisplayView.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwDisplayView_RunWorkerCompleted);
                bwDisplayView.RunWorkerAsync(lvSourceViews.SelectedItems[0].Tag);
            }
        }

        void bwDisplayView_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.lvSourceViews.SelectedIndexChanged += new EventHandler(lvSourceViews_SelectedIndexChanged);
            this.lvSourceViews.Enabled = true;
            this.Cursor = Cursors.Default;
            this.ccsb.SetMessage(string.Empty);
        }

        void bwDisplayView_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                // Gets current view data
                Entity currentSelectedView = (Entity)ListViewDelegates.GetSelectedItems(this.lvSourceViews)[0].Tag;
                string layoutXml = currentSelectedView["layoutxml"].ToString();
                string fetchXml = currentSelectedView.Contains("fetchxml") ? currentSelectedView["fetchxml"].ToString() : string.Empty;
                string currentEntityDisplayName = ListViewDelegates.GetSelectedItems(this.lvEntities)[0].Text.ToString();

                EntityMetadata currentEmd = entitiesCache.Find(delegate(EntityMetadata emd) { return emd.DisplayName.UserLocalizedLabel.Label == currentEntityDisplayName; });

                // Display layout xml
                TextBoxDelegates.SetText(this.txtSourceXmlLayout, XmlHelper.IndentXml(layoutXml));

                XmlDocument layoutDoc = new XmlDocument();
                layoutDoc.LoadXml(layoutXml);

                EntityMetadata emdWithItems = MetadataHelper.RetrieveEntity(currentEmd.LogicalName, this.service);

                ListViewItem item = new ListViewItem();
                item.Text = "preview";

                foreach (XmlNode columnNode in layoutDoc.SelectNodes("grid/row/cell"))
                {
                    ColumnHeader header = new ColumnHeader();

                    header.Text = MetadataHelper.RetrieveAttributeDisplayName(emdWithItems, columnNode.Attributes["name"].Value, fetchXml, this.service);
                    header.Width = int.Parse(columnNode.Attributes["width"].Value);

                    ListViewDelegates.AddColumn(lvSourceViewLayoutPreview, header);

                    item.SubItems.Add("preview");
                }

                ListViewDelegates.AddItem(lvSourceViewLayoutPreview, item);

                GroupBoxDelegates.SetEnableState(this.gbSourceViewLayout, true);
            }
            catch (SoapException error)
            {
                CommonDelegates.DisplayMessageBox(this, "Error while displaying view: " + error.Detail.SelectSingleNode("//description").InnerText, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception error)
            {
                CommonDelegates.DisplayMessageBox(this, "Error while displaying view: " + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void lvTargetViews_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ListViewDelegates.GetSelectedItems(lvTargetViews).Length > 0)
            {
                Entity currentView = (Entity)ListViewDelegates.GetSelectedItems(lvTargetViews)[0].Tag;

                string layoutXml = currentView["layoutxml"].ToString();

                TextBoxDelegates.SetText(this.txtTargetXmlLayout, XmlHelper.IndentXml(layoutXml));
            }
        }

        private void lvTargetViews_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            if (e.Item.Checked && e.Item.ForeColor == Color.Gray)
            {
                MessageBox.Show(this, "This view has not been defined as customizable. It can't be customized!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                e.Item.Checked = false;
            }

            if (ListViewDelegates.GetCheckedItems(this.lvTargetViews).Length > 0)
            {
                this.tsbSaveViews.Enabled = true;
                this.tsbPublishEntity.Enabled = true;
            }
            else
            {
                this.tsbSaveViews.Enabled = false;
                this.tsbPublishEntity.Enabled = false;
            }
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// Retrieve and set litview images
        /// </summary>
        private void FillImagesInListView()
        {
            System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetExecutingAssembly();

            Bitmap viewImage;
            Bitmap advFindImage;
            Bitmap assocImage;
            Bitmap defaultImage;
            Bitmap lookupImage;
            Bitmap quickImage;

            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039.gif"))
            {
                viewImage = new Bitmap(myStream);
                if (viewImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, viewImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, viewImage);
                }
            }

            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039_advFind.gif"))
            {
                advFindImage = new Bitmap(myStream);
                if (advFindImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, advFindImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, advFindImage);
                }
            }
            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039_associated.gif"))
            {
                assocImage = new Bitmap(myStream);
                if (assocImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, assocImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, assocImage);
                }
            }
            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039_default.gif"))
            {
                defaultImage = new Bitmap(myStream);
                if (defaultImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, defaultImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, defaultImage);
                }
            }
            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039_lookup.gif"))
            {
                lookupImage = new Bitmap(myStream);
                if (lookupImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, lookupImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, lookupImage);
                }
            }
            using (Stream myStream = myAssembly.GetManifestResourceStream("ViewLayoutReplicator2011.Images.ico_16_1039_quickFind.gif"))
            {
                quickImage = new Bitmap(myStream);
                if (quickImage != null)
                {
                    ListViewDelegates.AddImageToImageList(lvSourceViews, quickImage);
                    ListViewDelegates.AddImageToImageList(lvTargetViews, quickImage);
                }
            }
        }

        #endregion

        private void tsbRate_Click(object sender, EventArgs e)
        {
            Process.Start("http://viewlayoutreplicator.codeplex.com/releases");
        }

        private void tsbDiscuss_Click(object sender, EventArgs e)
        {
            Process.Start("http://viewlayoutreplicator.codeplex.com/discussions");
        }

        private void tsbReportBug_Click(object sender, EventArgs e)
        {
            Process.Start("http://viewlayoutreplicator.codeplex.com/WorkItem/Create");
        }

        private void tsbDonate_Click(object sender, EventArgs e)
        {
            string url = "";

            string business = "tanguy92@hotmail.com";  // your paypal email
            string description = "Donation%20for%20MSCRM%20Tools";            // '%20' represents a space. remember HTML!
            string country = "FR";                  // AU, US, etc.
            string currency = "EUR";                 // AUD, USD, etc.

            url += "https://www.paypal.com/cgi-bin/webscr" +
                "?cmd=" + "_donations" +
                "&business=" + business +
                "&lc=" + country +
                "&item_name=" + description +
                "&currency_code=" + currency +
                "&bn=" + "PP%2dDonationsBF";

            System.Diagnostics.Process.Start(url);

        }
    }
}
